package stericson.XDA.metamorph;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.util.zip.ZipException;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;

public class metamorph extends Activity {

	public boolean checked;
	static int PICK_REQUEST_CODE = 0;
	public static String homer = "/sdcard/AndroidThemes";
	static String TAG = "MetaMorph";
	public String path;
	public String themeControlFile;
	public static String themeDir;
	public String chosenTheme;
	public String chosenSubTheme;
	public String subThemePath;
	public String subThemeDir;
	public String pathtoit;
	public String mymsg;
	public String filetodownload;
	public boolean proceed;

	public boolean deleting;
	public boolean youdonot;
	public boolean flashallnow;
	public boolean disableflashall;
	public boolean existingtheme;
	public boolean rebootask = false;
	public boolean gohome = false;
	
	public String[] lines;

	public int counters = 0;
	public int subThemesFound = 0;

	ProgressDialog patience = null;

	private ViewGroup mGroup;
	private TextView mRomName;
	private TextView mRomVer;
	private ListView mList;
	private ViewGroup mGroupSub;
	private TextView mChosenSub;
	private TextView mChosenPath;
	private Button flashall;
	private Button existing;

	public TextView mChosen;

	// This controls our onscreen message at first boot.
	// This allows us to show a message to let users know what metamorph is
	// about
	public void Remembershownmessage(boolean enabled) {
		Settings.System.putInt(getContentResolver(), "SET_MESSAGE", enabled ? 1: 0);
	}

	public boolean mShowMessage() {
		return Settings.System.getInt(getContentResolver(), "SET_MESSAGE", 0) > 0;
	}

	// This controls our onscreen updates at first boot.
	// This allows us to show a message to let users know what's changed
	public void Remembershownupdate(boolean enabled) {
		Settings.System.putInt(getContentResolver(), "SET_UPDATE", enabled ? 4: 3);
	}

	public boolean mShowUpdate() {
		return Settings.System.getInt(getContentResolver(), "SET_UPDATE", 3) > 3;
	}

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {

		super.onCreate(savedInstanceState);

		// we set our view and we initialize everything
		setContentView(R.layout.main);

		mGroup = (ViewGroup) findViewById(R.id.ThemeInfo);
		mRomName = (TextView) findViewById(R.id.romname);
		mRomVer = (TextView) findViewById(R.id.romver);
		mGroup.setVisibility(View.GONE);
		mGroupSub = (ViewGroup) findViewById(R.id.SubThemeInfo);
		mChosenSub = (TextView) findViewById(R.id.chosen);
		mChosenPath = (TextView) findViewById(R.id.chosenpath);
		existing = (Button) this.findViewById(R.id.existing);
		flashall = (Button) this.findViewById(R.id.flashall);
		mChosen = (TextView) findViewById(R.id.chose);

		mGroupSub.setVisibility(View.GONE);
		flashall.setVisibility(View.GONE);

		mList = (ListView) findViewById(R.id.myListView);

		// initialize our buttons and define what they do.
		Button picktheme;

		picktheme = (Button) this.findViewById(R.id.pick);
		picktheme.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				if (!proceed) {
					SecondChecks();
					return;
				} else {
					gohome = true;
					disableflashall = false;
					existingtheme = false;
					picktime("/sdcard");
				}
			}
		});

		existing.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				if (!proceed) {
					SecondChecks();
					return;
				} else {
					gohome = true;
					disableflashall = false;
					ourMessages(
							"Pick a .thm file from one of the existing theme directories!",
							true, false);
					existingtheme = true;
					picktime("/sdcard/AndroidThemes");
				}
			}
		});

		flashall.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				counters = 0;
				flashallnow = true;
				flashingall();
			}
		});

		Button flashit;

		flashit = (Button) this.findViewById(R.id.flash);
		flashit.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				disableflashall = true;
				mymsg = "Applying " + chosenSubTheme + " theme ";
				flashitnow();
			}
		});

		// for debug purposes, lets us know what version user is running.
		Log.i(TAG, "Version 1.1 MetaMorph By Stericson & Kastro");

		// checks to see if we've shown the message or if we need to show the
		// message
		if (!mShowMessage()) {
			onetimemessage();
		} else {
			// Checks to see if we need to show our update message
			if (!mShowUpdate()) {
				updateMessage();
			} else {
				// run our first checks
				FirstChecks();
			}
		}
	}

	// First checks, lets remount for future use
	public void FirstChecks() {
		// Maybe this can be redone into a function or combined with sendshell?
		// Showing the please wait dialog
		// creating shell commands to send
		// sending commands to shell
		mymsg = "Initializing, please wait...";
		String[] commands = { "mount -o remount,rw -t yaffs2 /dev/block/mtdblock3 /system", };
		sendshell(commands, true, 0);
	}

	// We check for su and zip
	public void SecondChecks() {
		proceed = false;

		if (!isSdcardMounted()) {
			ourMessages("MetaMorph will not run without an SDcard!", false,
					true);
			return;
		}

		String[] pathtocheck = { "/system/bin/", "/system/xbin/",
				"/data/local/xbin/", "/data/local/bin/", "/system/sd/xbin/" };

		String[] whattocheck = { "su", "zip" };

		for (String fileToCheck : whattocheck) {

			boolean fileExists = false;
			for (String pathToCheck : pathtocheck) {
				File file = new File(pathToCheck, fileToCheck);
				if (file.exists()) {
					fileExists = true;
					break;
				}
			}

			if (!fileExists) {
				if (fileToCheck.equals("zip")) {

					proceed = false;
					filetodownload = fileToCheck;
					whaddado(true);
					return;
				}
				if (fileToCheck.equals("su")) {
					proceed = false;
					ourMessages(
							"It does not look like this phone has root access! The file: su could not be found!",
							false, true);
				}
			}
		}

		// This checks for AndExplorer
		String[] pathstocheck = { "/data/app", "system/app/",
				"/data/app-private/", "/system/sd/app/",
				"/system/sd/app-private/" };

		String[] whatstocheck = { "lysesoft.andexplorer.apk" };

		for (String fileToCheck : whatstocheck) {

			boolean fileExists = false;
			for (String pathToCheck : pathstocheck) {
				File file = new File(pathToCheck, fileToCheck);
				if (file.exists()) {
					fileExists = true;
					break;
				}
			}

			if (!fileExists) {
				if (fileToCheck.equals("lysesoft.andexplorer.apk")) {
					ourMessages(
							"Does not look like you have downloaded AndExporer from the market yet! MetaMorph needs this to run!",
							false, true);
				}
			}
		}
		// this must be true for the app to proceed
		proceed = true;
		if (proceed == true) {
			// Check for the AndroidThemes dir and if it does not exist we
			// create it
			File file = new File(homer);
			if (!file.exists()) {
				createdir(homer);
			}
		}
	}

	// is what it is
	public static boolean isSdcardMounted() {
		return android.os.Environment.getExternalStorageState().equals(
				android.os.Environment.MEDIA_MOUNTED);
	}

	// This is where we will let the user pick a theme.
	public void picktime(String path) {
		Intent intent = new Intent();
		Uri startDir = Uri.fromFile(new File(path));
		intent.setAction(Intent.ACTION_PICK);
		intent.setDataAndType(startDir, "vnd.android.cursor.dir/*");
		// intent.setDataAndType(startDir, "file://");
		// Title
		intent.putExtra("explorer_title", "Select a file");
		// Optional colors
		intent.putExtra("browser_title_background_color", "440000AA");
		intent.putExtra("browser_title_foreground_color", "FFFFFFFF");
		intent.putExtra("browser_list_background_color", "00000066");
		// Optional font scale
		intent.putExtra("browser_list_fontscale", "120%");
		// Optional 0=simple list, 1 = list with filename and size, 2 = list
		// with filename, size and date.
		intent.putExtra("browser_list_layout", "2");
		startActivityForResult(intent, PICK_REQUEST_CODE);
	}

	// this is called when we have picked a theme or cancelled
	protected void onActivityResult(int requestCode, int resultCode,
			Intent intent) {
		if (requestCode == PICK_REQUEST_CODE) {
			if (resultCode == RESULT_OK) {
				Uri uri = intent.getData();
				String type = intent.getType();
				Log.i(TAG, "Pick completed: " + uri + " " + type);
				if (uri != null) {
					path = uri.toString();
					if (path.toLowerCase().startsWith("file://")) {
						// Selected file path is below
						path = (new File(URI.create(path))).getAbsolutePath();

						// this gives us our theme dir path
						pathtoit = (new File(path)).getParent();

						if (!existingtheme) {
							// This checks that the file picked is a zip file.
							if (!path.endsWith(".zip")) {
								ourMessages(
										"That is not a valid Theme file! You must pick a file that ends with .zip!",
										true, true);
								picktime("/sdcard");
								return;
							}
							chosenTheme = path.replace(pathtoit + "/", "");
							Log.i(TAG, chosenTheme);

							themeControlFile = chosenTheme.replace(".zip",
									".thm");
							themeDir = chosenTheme.replace(".zip", "");
							Log.i(TAG, themeDir);

							File file = new File(homer + "/" + themeDir);

							// this checks for existing theme and if it exists
							// asks for input
							// by calling the method whaddado()
							if (!file.exists()) {
								createdir(homer + "/" + themeDir);
								Log.i(TAG, homer + "/" + themeDir);

							} else {
								// method to call to get user input
								whaddado(false);
								// exit the method
								return;
							}
							// we seperate these vars into a method of its own
							// to reduce code.
							finishVars();

						} else {
							// This checks to make sure that the user picked a
							// .thm file.
							if (!path.endsWith(".thm")) {
								ourMessages(
										"That is not a valid Theme Control file! You must pick a file that ends with .thm!",
										true, true);
								picktime("/sdcard/AndroidThemes");
								return;
							}
							String temp = path.replace(pathtoit + "/", "");
							chosenTheme = temp.replace("thm", "zip");
							Log.i(TAG, chosenTheme);
							listsub(true);
						}
					}
				} else {
					// uh oh, we did not pick a theme
					Log.i(TAG, "Back from pick with cancel status");
				}
			}
		}
	}

	// this lists the subthemes by reading them from the .thm file
	protected void listsub(boolean mexisting) {
		if (!mexisting) {
			// dismiss the dialog
			patience.dismiss();
			try {
				// read from, not write to, the theme file
				readwrite(homer + "/" + themeDir + "/" + themeControlFile,
						false);
			} catch (Exception e) {
				// if we cannot read from the file somethings wrong, we cannot
				// recover
				youdonot = true;
				Log.i(TAG, homer + "/" + themeDir + "/" + themeControlFile);
				// keep things clean, delete the directory
				deleteme(homer + "/" + themeDir);
				patience.dismiss();
				ourMessages("Theme control file could not be read!", true,
						false);
				ourMessages(
						"The Theme you selected is invalid because the theme control file may be missing.",
						false, true);
				return;
			}
		} else {
			try {
				readwrite(path, false);
			} catch (Exception e) {
				// if we cannot read from the file somethings wrong, we cannot
				// recover
				ourMessages("Theme control file could not be read!", true,
						false);
				ourMessages(
						"The Theme you selected is invalid because the theme control file may be missing.",
						false, true);
			}
		}
			// everythings good, we change our view
			mGroup.setVisibility(View.VISIBLE);
			existing.setVisibility(View.GONE);
			// Let the user know what theme they have chosen
			mChosen.setText("You have chosen: " + chosenTheme);
			mChosen.setVisibility(View.VISIBLE);
			if (!disableflashall) {
				flashall.setVisibility(View.VISIBLE);
				existing.setVisibility(View.GONE);
			} else {
				existing.setVisibility(View.VISIBLE);
				flashall.setVisibility(View.GONE);
			}
			// create the list of subthemes
			ArrayAdapter<String> myArrayAdapter = new ArrayAdapter<String>(
					this, android.R.layout.simple_list_item_1, lines);
			mList.setAdapter(myArrayAdapter);

		// tells us what the user chose
		mList.setOnItemClickListener(new OnItemClickListener() {
			public void onItemClick(AdapterView<?> parent, View v,
					int position, long id) {
				// change our view again
				existing.setVisibility(View.VISIBLE);
				flashall.setVisibility(View.GONE);
				chosenSubTheme = lines[position];
				mGroup.setVisibility(View.GONE);

				for (int i = 0; i < lines.length; i++) {

					if (lines[i].equals(chosenSubTheme)) {

						if (!existingtheme) {
							try {
								readwrite(homer + "/" + themeDir + "/"
										+ themeControlFile, true);
							} catch (IOException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						} else {
							try {
								readwrite(path, true);
							} catch (IOException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						}

						// we check to find out whether the target file is a jar
						// or apk
						if (chosenSubTheme.endsWith(".apk")) {
							subThemeDir = chosenSubTheme.replace(".apk", "");
						} else if (chosenSubTheme.endsWith(".jar")) {
							subThemeDir = chosenSubTheme.replace(".jar", "");
						} else {
							ourMessages(
									"Uhhh...something isnt right here... we were expecting .apk or .jar but instead we got "
											+ chosenSubTheme, false, true);
						}

						// change view again
						mGroupSub.setVisibility(View.VISIBLE);
						mChosenSub.setText("You have chosen to use the theme "
								+ chosenSubTheme + " Which is a Sub-Theme of "
								+ chosenTheme);
						mChosenPath
								.setText("This theme will target the APK/jar: "
										+ chosenSubTheme
										+ " Which is located here: "
										+ subThemePath);

					}

				}
			}
		});
	}

	// background threads use this Handler to post messages to
	// the main application thread
	private final Handler mHandler = new Handler();

	// post this to the Handler when the background thread completes
	private final Runnable mCompleteRunnable = new Runnable() {
		public void run() {
			if (proceed) {
				if (!deleting) {
					onThreadCompleted();
				} else {
					// Things are a little funny here, bear(haha) with me
					// since we call deleteme() at different points
					// we need to know when the thread completes so
					// these variables let us know.
					if (!youdonot) {
						deleting = false;
						youdado();
					} else {
						youdonot = false;
						deleting = false;
					}
				}
			} else {
				// call second checks after mounting system as RW
				SecondChecks();
			}
		}
	};

	// background threads use this Handler to post messages to
	// the main application thread
	// this is a custom handler for downloading our files :)
	private final Handler mHandler1 = new Handler();
	// and this goes with it
	private final Runnable mCompleteFailed1 = new Runnable() {
		public void run() {
			patience.dismiss();
			ourMessages(
					"What the heck?! Do we have a connection? Something went wrong.",
					false, true);
		}
	};

	// post this to the Handler when the background thread completes
	// and this goes with it as well
	private final Runnable mCompleteRunnable1 = new Runnable() {
		public void run() {
			// dismiss the current showing Dialog
			patience.dismiss();
			// Maybe this can be redone into a fuction or combined with
			// sendshell?
			// Showing the please wait dialog
			// creating shell commands to send
			// sending commands to shell
			mymsg = "Setting up file: " + filetodownload;
			String[] commands = {
					"cp /sdcard/" + filetodownload + " /system/bin",
					"rm /sdcard/" + filetodownload,
					"chmod 0755 /system/bin/" + filetodownload };
			sendshell(commands, true, 0);
		}
	};

	// this is called when our threads finish.
	// this is only called when we want to call it :)
	// selective calling, kinda like selective hearing.
	private void onThreadCompleted() {
		if (!flashallnow) {
			if (rebootask) {
				rebootnow();
			} else {
				if (!existingtheme) {
					listsub(false);
				} else {
					listsub(true);
				}
			}
		} else {
			if (counters < subThemesFound) {
				patience.dismiss();
				flashingall();
			} else {
				patience.dismiss();
				if (!rebootask) {
					ourMessages("All themes Applied!", true, false);
					mGroup.setVisibility(View.GONE);
					if (!disableflashall) {
						flashall.setVisibility(View.GONE);
					}
					existing.setVisibility(View.VISIBLE);
				} else {
					rebootnow();
				}
			}
		}
	}

	// this flashes all the themes, we keep it out of the flashit
	// button event so anyone can call it.
	public void flashitnow() {
		File file;
		File dir = null;
		if (!existingtheme) {
			file = new File(homer + "/" + themeDir + "/" + subThemeDir);
		} else {
			file = new File(pathtoit + "/" + subThemeDir);
		}
		// This checks to make sure the subtheme that the .thm file has
		// told us we are to flash/install actually exists in the theme.
		// this will skip it if the subtheme dir doesnt exist.
		// can't stop us! Keep moving forward!
		if (file.exists()) {
			// This checks to see if the target apk exists.
			// if not it skips it.
			File file1 = new File(subThemePath + chosenSubTheme);
			if (file1.exists()) {
				if (!existingtheme) {
					// Maybe this can be redone into a method or combined with
					// sendshell?
					// creating shell commands to send
					// sending commands to shell
					String[] commands = {
							"cd " + homer + "/" + themeDir + "/" + subThemeDir,
							"zip -r " + subThemePath + chosenSubTheme + " "
									+ "./*" };
					sendshell(commands, true, 3000);
				} else {
					// Maybe this can be redone into a method or combined with
					// sendshell?
					// creating shell commands to send
					// sending commands to shell
					String[] commands = {
							"cd " + pathtoit + "/" + subThemeDir,
							"zip -r " + subThemePath + chosenSubTheme + " "
									+ "./*" };
					sendshell(commands, true, 3000);
				}
			} else {
				ourMessages("The target " + subThemePath + chosenSubTheme
						+ "Does not Exist, Skipping...", true, true);
				Log.i(TAG, "The target " + subThemePath + chosenSubTheme
						+ "Does not Exist, Skipping...");
				onThreadCompleted();
			}
		} else {
			if (!existingtheme) {
				ourMessages("The Thm file says we are looking for " + homer
						+ "/" + themeDir + "/" + subThemeDir
						+ " in this theme but it does not Exist, Skipping...",
						true, true);
				Log.i(TAG, "The Thm file says we are looking for " + homer
						+ "/" + themeDir + "/" + subThemeDir
						+ " in this theme but it does not Exist, Skipping");
				dir = new File(homer + "/" + themeDir);
				String[] children = dir.list();

				Log.i(TAG, "This is the contents of the Theme Folder:");
				for (String dirlisting : children) {
					Log.i(TAG, dirlisting);
				}
				onThreadCompleted();
			} else {
				ourMessages("The Thm file says we are looking for " + pathtoit
						+ "/" + subThemeDir
						+ " in this theme but it does not Exist, Skipping...",
						true, true);
				Log.i(TAG, "The Thm file says we are looking for " + pathtoit
						+ "/" + subThemeDir
						+ " in this theme but it does not Exist, Skipping");
				dir = new File(pathtoit);
				String[] children = dir.list();

				Log.i(TAG, "This is the contents of the Theme Folder:");
				for (String dirlisting : children) {
					Log.i(TAG, dirlisting);
				}
				onThreadCompleted();
			}

		}
	}

	private void flashingall() {
		if (counters < subThemesFound) {
			chosenSubTheme = lines[counters];
			counters++;

			if (chosenSubTheme.endsWith(".apk")) {
				subThemeDir = chosenSubTheme.replace(".apk", "");
			} else if (chosenSubTheme.endsWith(".jar")) {
				subThemeDir = chosenSubTheme.replace(".jar", "");
			} else {
				ourMessages(
						"Uhhh...something isnt right here... we were expecting .apk or .jar but instead we got "
								+ chosenSubTheme, false, true);
			}

			if (existingtheme) {
				try {
					readwrite(path, true);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			} else {
				try {
					readwrite(homer + "/" + themeDir + "/" + themeControlFile,
							true);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			mymsg = "Applying " + chosenSubTheme + " theme " + counters
					+ " of " + subThemesFound;
			flashitnow();
		}
	}

	// method to create directories.
	// JF - Revision.

	public void createdir(String tocreate) {
		File fileToCreate = new File(tocreate);
		if (!fileToCreate.exists()) {
			Log.i(TAG, "attempt to create directory: " + tocreate);

			if (fileToCreate.mkdirs()) {
				Log.i(TAG, "Dir " + tocreate + " was created.");
			} else {
				ourMessages("Dir " + tocreate + " could not be created.",
						false, false);
				Log.i(TAG, "Dir " + tocreate + " was not created.");
			}
		}
	}

	// method to delete directories

	public void deleteme(final String args) {
		mymsg = "Deleting Dir: " + args;
		patience = ProgressDialog.show(metamorph.this, "Please Wait", mymsg,
				true);
		Thread t = new Thread() {
			public void run() {
				// perform expensive tasks in a background thread
				deleting = true;
				deleteDirectory(new File(args));
				Log.i(TAG, "Dir " + args + " was deleted.");

				// let the UI know the task is complete
				patience.dismiss();
				mHandler.post(mCompleteRunnable);
			}
		};
		t.start();
	}

	// called by deleteme, helps it delete directories that are not empty
	public boolean deleteDirectory(File path) {
		if (path.exists()) {
			File[] files = path.listFiles();
			for (int i = 0; i < files.length; i++) {
				if (files[i].isDirectory()) {
					deleteDirectory(files[i]);
				} else {
					files[i].delete();
				}
			}
		}
		return (path.delete());
	}

	// This reads a file and throws it into a variable.
	public void readwrite(String filename, boolean findpath) throws IOException {
		FileInputStream in = new FileInputStream(filename);
		BufferedReader br = new BufferedReader(new InputStreamReader(in));

		int count = 0;
		String strLine;

		if (!findpath) {
			subThemesFound = 0;
			int oddnumbers = 3;
			int increaseby = 2;
			br.mark(5000);
			// This tells me how many subthemes are in the theme.
			while ((strLine = br.readLine()) != null) {
				if (strLine.length() <= 1) {
					break;
				}
				count++;
				if (count == oddnumbers) {
					oddnumbers = (oddnumbers + increaseby);
					subThemesFound++;
				}
			}
			// This puts the sub themes into an array for the listview.
			// I'm sure we could simplify this whole process.
			lines = new String[subThemesFound];
			count = 0;
			oddnumbers = 3;
			br.reset();
			for (int j = 0; j < lines.length;) {
				if ((strLine = br.readLine()) != null) {
					count++;
					if (count == oddnumbers) {
						oddnumbers = (oddnumbers + increaseby);
						lines[j] = strLine;
						j++;
						Log.i(TAG, strLine);

					}
					if (count == 1) {
						mRomName.setText("Made for Rom: " + strLine);
					} else if (count == 2) {
						mRomVer.setText("Made for Version: " + strLine);
					}

				}

			}
			in.close();
		} else {
			Log.i(TAG, "This is the Theme Control file:");
			while ((strLine = br.readLine()) != null) {
				if (strLine.length() <= 1) {
					break;
				}
				Log.i(TAG, strLine);
				if (strLine.equals(chosenSubTheme)) {
					if (strLine.equals("framework-res.apk")) {
						rebootask = true;
					}
					subThemePath = br.readLine();
				}
			}
		}
	}

	// This is Our Custom Toast for error notifications & message notifications.
	public void ourMessages(String errormessage, boolean recoverable,
			boolean longtoast) {
		LayoutInflater inflater = getLayoutInflater();
		View layout = inflater.inflate(R.layout.toast_layout,
				(ViewGroup) findViewById(R.id.toast_layout_root));

		ImageView image = (ImageView) layout.findViewById(R.id.image);
		image.setImageResource(R.drawable.icon);
		TextView text = (TextView) layout.findViewById(R.id.text);
		text.setText(errormessage);

		Toast toast = new Toast(getApplicationContext());
		toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
		if (longtoast) {
			toast.setDuration(Toast.LENGTH_LONG);
		} else {
			toast.setDuration(Toast.LENGTH_SHORT);
		}
		toast.setView(layout);
		toast.show();
		if (!recoverable) {
			checked = false;
			finish();
		}
	}

	// this is used to ask the user if he wants to pdate or replace an existing
	// theme
	public void whaddado(boolean downloadfile) {
		if (!downloadfile) {
			new AlertDialog.Builder(metamorph.this)
					.setTitle("What to do?")
					.setMessage(
							"The theme "
									+ chosenTheme
									+ " already exists. Do you want to update it or replace it?")
					.setNegativeButton("Replace",
							new DialogInterface.OnClickListener() {

								public void onClick(DialogInterface dialog,
										int whichButton) {
									deleteme(homer + "/" + themeDir);
								}
							}).setPositiveButton("Update",
							new DialogInterface.OnClickListener() {

								public void onClick(DialogInterface dialog,
										int whichButton) {
									Log.i(TAG, "Updating " + homer + "/"
											+ themeDir);
									finishVars();
								}
							}).show();
		} else {
			new AlertDialog.Builder(metamorph.this)
					.setTitle("What to do?")
					.setMessage(
							filetodownload
									+ " was not found on your phone. MetaMorph needs this to run."
									+ " Would you like me to download it and set it up for you?")
					.setPositiveButton("Yes",
							new DialogInterface.OnClickListener() {

								public void onClick(DialogInterface dialog,
										int whichButton) {

									mymsg = "Downloading the file: "
											+ filetodownload;
									patience = ProgressDialog.show(
											metamorph.this, "Please Wait",
											mymsg, true);
									Thread t = new Thread() {
										public void run() {
											// perform expensive tasks in a
											// background thread
											downloadfiles();
										}
									};
									t.start();
								}
							}).setNegativeButton("no",
							new DialogInterface.OnClickListener() {

								public void onClick(DialogInterface dialog,
										int whichButton) {

									ourMessages(
											"Metamorph cannot be run without "
													+ filetodownload
													+ " Please correct this before running MetaMorph again.",
											false, true);
								}

							}).show();
		}
	}

	// creates the theme dir and calls finishvars
	public void youdado() {
		createdir(homer + "/" + themeDir);
		Log.i(TAG, homer + "/" + themeDir);
		finishVars();
	}

	// We finish our vars here because the user can make decisions that halted
	// our original process. so we wait for user input and then decide what to
	// do.
	public void finishVars() {

		// this is the thm control file. This isnt created if we pick an
		// existing theme.
		themeControlFile = themeDir + ".thm";

		mymsg = "Unzipping " + chosenTheme;
		patience = ProgressDialog.show(metamorph.this, "Please Wait", mymsg,
				true);
		Thread t = new Thread() {
			public void run() {
				try {
					Unzipit.unzipper(path);
				} catch (ZipException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				// let the UI know the task is complete
				mHandler.post(mCompleteRunnable);
			}
		};
		t.start();
	}

	// this is used to download zip.
	// However this can be used to download any file.
	public void downloadfiles() {

		try {
			// url to download from + the file we want to download
			URL n = new URL("http://www.relentlessaddictions.com/"
					+ filetodownload);
			HttpURLConnection ulr = (HttpURLConnection) n.openConnection();
			ulr.setRequestMethod("GET");
			ulr.setDoOutput(true);
			ulr.connect();
			// where we want to save the file + the file name
			FileOutputStream f = new FileOutputStream(new File("/sdcard/"
					+ filetodownload));
			InputStream urlin = ulr.getInputStream();

			byte[] buffer = new byte[1024];
			int len1 = 0;
			while ((len1 = urlin.read(buffer)) != -1) {
				f.write(buffer, 0, len1);
			}
			f.close();
			urlin.close();
		} catch (IOException e) {
			// uhh we failed, let the user know.
			mHandler1.post(mCompleteFailed1);
			return;
		}
		// let the UI know the task is complete
		mHandler1.post(mCompleteRunnable1);

	}

	// Quite obviously this presents the user with a dialog asking if we want to
	// reboot
	// if the user says yes then we reboot, otherwise we will not.
	public void rebootnow() {
		new AlertDialog.Builder(metamorph.this)
				.setTitle("What to do?")
				.setMessage(
						"I've detected you may have applied a theme to framework-res.apk, which requires a reboot to see the changes made. Would you like to reboot now?")
				.setPositiveButton("Yes",
						new DialogInterface.OnClickListener() {

							public void onClick(DialogInterface dialog,
									int whichButton) {
								// Maybe this can be redone into a fuction or
								// combined with sendshell?
								// Showing the please wait dialog
								// creating shell commands to send
								// sending commands to shell
								mymsg = "Rebooting";
								String[] commands = { "reboot" };
								sendshell(commands, true, 0);
							}
						}).setNegativeButton("no",
						new DialogInterface.OnClickListener() {

							public void onClick(DialogInterface dialog,
									int whichButton) {
								if (flashallnow) {
									// we flashed all themes, no need to stay
									// running.
									ourMessages("Ok! We are all done then!",
											true, false);
									rebootask = false;
									mGroup.setVisibility(View.GONE);
									if (!disableflashall) {
										flashall.setVisibility(View.GONE);
									}
									existing.setVisibility(View.VISIBLE);
								} else {
									// we flashed a theme, user might want us to
									// stay running.
									rebootask = false;
									// we call this to let it be known we are
									// done with the thread.
									onThreadCompleted();
								}
							}

						}).show();
	}

	// Used to send commands to shell.
	// This also runs in its own thread to prevent ANR.
	public void sendshell(final String[] commands, final boolean postmessage,
			final int sleeptime) {
		patience = ProgressDialog.show(metamorph.this, "Please Wait", mymsg,
				true);
		Thread t = new Thread() {
			public void run() {
				// perform expensive tasks in a background thread
				ShellInterface.doExec(commands, sleeptime);
				// let the UI know the task is complete
				if (postmessage) {
					patience.dismiss();
					mHandler.post(mCompleteRunnable);
				} else {
					return;
				}
			}
		};
		t.start();
	}

    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && gohome) {
        	gohome = false;
    		mGroup.setVisibility(View.GONE);
    		mGroupSub.setVisibility(View.GONE);
    		flashall.setVisibility(View.GONE);
    		existing.setVisibility(View.VISIBLE);
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

	// **********************************************************
	// Dialogs
	// **********************************************************

	// this is our one time message;
	public void onetimemessage() {
		new AlertDialog.Builder(metamorph.this)
				.setTitle("Welcome to MetaMorph!")
				.setMessage(
						"Hi, and welcome to MetaMorph, I have noticed that this is your first time using this so "
								+ "I wanted to run through a few things real quick before we get started. MetaMorph is a"
								+ " project designed to theme your phone without having to going into recovery or push"
								+ " custom apks to your phone. Rather, MetaMorph allows for you to replace specific files"
								+ " inside of the apks in real time using themes designed for MetaMorph. Before using this "
								+ "for the first time MAKE A NANDROID BACKUP! Another very important thing to point out "
								+ "is that this project asks for root ALOT..most of the work it does requires root and"
								+ " I have to ask for it each time. That being said, this program works best by selecting"
								+ " always allow when it asks for root. at any rate, please enjoy the program and report all bugs, "
								+ "suggestion, and problems found. ")
				.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int whichButton) {
						// remember we have shown the message
						Remembershownmessage(true);
						// Checks to see if we need to show our update message
						if (!mShowUpdate()) {
							updateMessage();
						} else {
							// run our first checks
							FirstChecks();
						}
					}
				}).show();
	}

	// This is our update message
	public void updateMessage() {
		new AlertDialog.Builder(metamorph.this)
				.setTitle("MetaMorph Updates!")
				.setMessage(
						"In V1.1 a few bugs have been fixed. We fixed a bug where the chosen theme was not being " +
						" displayed when a user picks an existing theme. We also added back in the ability to " +
						"use the back button to start from the beginning of MetaMorph.")
				.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int whichButton) {
						// remember we have shown the updates
						Remembershownupdate(true);
						// run our first checks
						FirstChecks();
					}
				}).show();
	}
}
